#include "INode.h"
#include "Exception.h"

void INode::updateModifyTime()
{
	modifyTime = Tool::getCurrentNanoSecond();
}

void INode::updateAccessTime()
{
	accessTime = Tool::getCurrentNanoSecond();
}

void INode::setMode(uint16_t mode)
{
	typeMode = ((typeMode >> 9) << 9) | mode;
}

uint16_t INode::getMode() const
{
	return typeMode & 0777;
}

std::string INode::getModeStr() const
{
	std::string ret = "";
	if(isDirectory()) ret += "d";
	else if(isFile()) ret += "-";
	for(int i = 6; i >= 0; i -= 3) {
		if(typeMode >> i & READ) ret += "r";
		else ret += "-";
		if(typeMode >> i & WRITE) ret += "w";
		else ret += "-";
		if(typeMode >> i & EXECUTE) ret += "x";
		else ret += "-";
	}
	return ret;
}

std::string INode::getSizeStr() const
{
	if(size > 1024 * 1024 * 1024) return std::format("{:.2f}GB", size / 1024.0 / 1024.0 / 1024.0);
	if(size > 1024 * 1024) return std::format("{:.2f}MB", size / 1024.0 / 1024.0);
	if(size > 1024) return std::format("{:.2f}KB", size / 1024.0);
	return std::format("{}B", size);
}

bool INode::isDirectory() const
{
	return (typeMode >> 9 & 1) == DIRECTORY;
}

bool INode::isFile() const
{
	return (typeMode >> 9 & 1) == FILE;
}

bool INode::isExecutable() const
{
	return (typeMode >> 6 & EXECUTE) || (typeMode >> 3 & EXECUTE) || (typeMode & EXECUTE);
}

bool INode::isExecutableFile() const
{
	return isFile() && isExecutable();
}

bool INode::checkFilePermission(uint32_t sid, uint8_t gid, uint8_t uid, uint8_t op) const
{
	if(gid == 0) return true;
	if (sid == systemID && uid == userID) {
		return typeMode >> 6 & op;
	}
	if (sid == systemID && gid == groupID) {
		return typeMode >> 3 & op;
	}
	return typeMode & op;
}

bool INode::checkReadable(uint32_t sid, uint8_t gid, uint8_t uid) const
{
	return checkFilePermission(sid, gid, uid, INode::READ);
}

bool INode::checkWriteable(uint32_t sid, uint8_t gid, uint8_t uid) const
{
	return checkFilePermission(sid, gid, uid, INode::WRITE);
}

bool INode::checkExecutable(uint32_t sid, uint8_t gid, uint8_t uid) const
{
	return checkFilePermission(sid, gid, uid, INode::EXECUTE);
}

uint64_t INode::getReadAddr()
{
	updateAccessTime();
	return addr;
}

uint64_t INode::getWriteAddr()
{
	updateModifyTime();
	return addr;
}

void INode::initRootDirectory()
{
	createTime = modifyTime = accessTime = Tool::getCurrentNanoSecond();
	addr = 0;
	size = 0;
	pages = 0;
	parentID = 0;
	systemID = 0;
	typeMode = DIRECTORY << 9 | DEFAULT_ROOT_MODE;
	groupID = 0;
	userID = 0;
}

void INode::initDirectory(uint32_t sid, uint8_t gid, uint8_t uid)
{
	createTime = modifyTime = accessTime = Tool::getCurrentNanoSecond();
	addr = 0;
	size = 0;
	pages = 0;
	parentID = 0;
	systemID = sid;
	typeMode = DIRECTORY << 9 | DEFAULT_DIR_MODE;
	groupID = gid;
	userID = uid;
}

void INode::initFile(uint32_t sid, uint8_t gid, uint8_t uid)
{
	createTime = modifyTime = accessTime = Tool::getCurrentNanoSecond();
	addr = 0;
	size = 0;
	pages = 0;
	parentID = 0;
	systemID = sid;
	typeMode = FILE << 9 | DEFAULT_FILE_MODE;
	groupID = gid;
	userID = uid;
}

std::ostream &operator<<(std::ostream &os, const INode &nd)
{
	os << "IND{";
	os << "ctime: " << nd.createTime << ", ";
	os << "mtime: " << nd.modifyTime << ", ";
	os << "atime: " << nd.accessTime << ", ";
	os << "addr: " << nd.addr << ", ";
	os << "size: " << nd.size << ", ";
	os << "pages: " << nd.pages << ", ";
	os << "sid: " << nd.systemID << ", ";
	os << "gid: " << (int)nd.groupID << ", ";
	os << "uid: " << (int)nd.userID << ", ";
	os << "type: " << ((nd.typeMode >> 9 & 1) == INode::DIRECTORY ? "Directory" : "File") << ", ";
	os << "mode: " << std::bitset<3>(nd.typeMode >> 6) << "-" << std::bitset<3>(nd.typeMode >> 3) << "-" << std::bitset<3>(nd.typeMode) << ", ";
	os << "next: " << nd.parentID;
	os << "}";
	return os;
}
